﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using ReactiveCode;
using ReactiveCode.Expressions;
using ReactiveCode.Observables;

using E = ReactiveCode.Expressions.ReactiveExpression;

namespace TimeZoneConverter
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        TimeZoneInfo localTimeZone;
        ReactiveHost _reactive = new ReactiveHost();

        public MainWindow()
        {
            InitializeComponent();

            srcTime.Text = DateTime.Now.ToLongTimeString();

            foreach (var tz in TimeZoneInfo.GetSystemTimeZones())
            {
                targetTimeZone.Items.Add(tz);
                if (tz.DisplayName.Contains("Tashkent"))
                    targetTimeZone.SelectedItem = tz;

                srcTimeZone.Items.Add(tz);
                if (tz.BaseUtcOffset == TimeZoneInfo.Local.BaseUtcOffset)
                    srcTimeZone.SelectedItem = localTimeZone = tz;
            }

            // create the rule set 
            // and save a reference to it to prevent it to be collected by the GC
            _reactive.Call(MyReactiveMethod());
        }

        struct DateTimeTryParseResult
        {
            public bool ReturnValue;
            public DateTime Value;

            public override string ToString()
            {
                return "{ ReturnValue = " + ReturnValue + ", Value = " + Value + " }";
            }
        }
        static DateTimeTryParseResult DateTimeTryParse(string text)
        {
            DateTimeTryParseResult result;
            result.ReturnValue = DateTime.TryParse(text, out result.Value);
            return result;
        }

        ReactiveMethodDefinition<Unit> _myReactiveMethod;
        ReactiveMethodDefinition<Unit> MyReactiveMethod()
        {
            if (_myReactiveMethod == null)
            {
                _myReactiveMethod = new ReactiveMethodDefinition<Unit>(delegate
                {
                    // variables of the live method
                    // the same as in the TimeZoneConverter.LiveCode plus a couple of additional ones
                    var sourceTime = new Variable<DateTime>();
                    var tryParseResult = new Variable<DateTimeTryParseResult>();
                    var sourceTimeZone = new Variable<TimeZoneInfo>();
                    var destinationTimeZone = new Variable<TimeZoneInfo>();

                    // some values used in sync rules
                    //ObservableFactory<DateTime> dateTimeNow = f => Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1), dispatcherScheduler)
                    //    .Filter(f).Select(__ => DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified));

                    // method groups a set of sync rules. 
                    // its content recalls AST of the MainWindow.SetupSync in the TimeZoneConverter.LiveCode project
                    return E.Block
                    (
                        // if (useCurrentTime.IsChecked == true)
                        E.If(() => useCurrentTime.IsChecked == true,

                            E.Block(
                                E.Assignment(() => srcTime.IsEnabled, false),
                                E.Assignment(() => srcTimeZone.IsEnabled, false),
                                E.Assignment(() => srcTimeZone.SelectedItem, localTimeZone),
                                E.Assignment(() => sourceTime.Value, () => DateTime.Now),
                                E.Assignment(() => srcTime.Text, () => sourceTime.Value.ToLongTimeString())
                            ),

                            E.Block(
                                E.Assignment(() => tryParseResult.Value, () => DateTimeTryParse(srcTime.Text)),

                                 // if (!tryParseResult.Result)
                                E.If(() => !tryParseResult.Value.ReturnValue,
                                    E.Block(
                                        E.Assignment(() => srcTime.Background, Brushes.LightPink),
                                        E.Return()
                                    ),

                                    E.Assignment(() => sourceTime.Value, () => tryParseResult.Value.Value)
                                )
                            )
                        ),

                        E.Assignment(() => sourceTimeZone.Value, () => (TimeZoneInfo) srcTimeZone.SelectedItem),
                        E.Assignment(() => destinationTimeZone.Value, () => (TimeZoneInfo) targetTimeZone.SelectedItem),

                        E.If(() => sourceTimeZone != null && destinationTimeZone != null,
                             E.Block(
                                 E.Assignment(() => sourceTime.Value, () => DateTime.SpecifyKind(sourceTime.Value, DateTimeKind.Unspecified)),
                                 E.Imperative(
                                     () => TimeZoneInfo.ConvertTime(sourceTime.Value, sourceTimeZone.Value, destinationTimeZone.Value).ToLongTimeString(),
                                     x => targetTime.Text = x
                                 )
                             )
                        )
                    );
                });
            }
            return _myReactiveMethod;
        }
    }
}
